<?php
namespace Org;

/**
 * RSA 加签验签、加密解密功能
 * @author leeyi <leeyisoft@qq.com>
 */
class RSAEncrypt
{
    private $privateKeyFile;
    private $publicKeyFile;

    private $privateKey;
    private $publicKey;

    // 最近错误信息
    protected $error = '';

     /**
     * 架构函数
     * @param array $options 缓存参数
     * @access public
     * @author leeyi <leeyisoft@qq.com>
     */
    public function __construct($privateKeyFile = '', $publicKeyFile = '')
    {
        // Enter passphrase
        $this->privateKeyFile = empty($privateKeyFile)
            ? dirname(dirname(__FILE__)).'/rsa_key/rsa_pri_key.pem' : $privateKeyFile;
        $this->publicKeyFile = empty($publicKeyFile)
            ? dirname(dirname(__FILE__)).'/rsa_key/rsa_pub_key.pem' : $publicKeyFile;
    }

    /**
     * 返回模型的错误信息
     * @access public
     * @return string
     * @author leeyi <leeyisoft@qq.com>
     */
    public function getError()
    {
        return $this->error;
    }
    /**
     * RSA签名
     * $data签名数据(需要先排序，然后拼接)
     * 签名用私钥，必须是没有经过pkcs8转换的私钥
     * 最后的签名，需要用base64编码
     * @return Sign签名
     * @author leeyi <leeyisoft@qq.com>
     */
    public function sign($data)
    {
        $data = is_array($data) ? json_encode($data) : $data;
        //读取私钥文件
        $privateKey = $this->getPrivateKey();
        if (empty($privateKey)) {
            return false;
        }
        //转换为openssl密钥，必须是没有经过pkcs8转换的私钥
        $res = openssl_get_privatekey($privateKey);
        //调用openssl内置签名方法，生成签名$sign
        openssl_sign($data, $sign, $res, OPENSSL_ALGO_SHA1);
        //释放资源
        openssl_free_key($res);
        //base64编码
        $sign = base64_encode($sign);

        return $sign;
    }

    /**
     * RSA验签
     * $data待签名数据(需要先排序，然后拼接)
     * $sign需要验签的签名,需要base64_decode解码
     * 验签用公钥
     * @return 验签是否通过 bool值
     * @author leeyi <leeyisoft@qq.com>
     */
    public function verify($data, $sign)
    {
        $data = is_array($data) ? json_encode($data) : $data;
        // 读取public key
        $publicKey = $this->getPublicKey();
        if (empty($publicKey)) {
            return false;
        }
        //转换为openssl格式密钥
        $res = openssl_get_publickey($publicKey);
        //调用openssl内置方法验签，返回bool值
        $result = (bool)openssl_verify($data, base64_decode($sign), $res, OPENSSL_ALGO_SHA1);
        //释放资源
        openssl_free_key($res);
        //返回资源是否成功
        return $result;
    }

    /**
     * RSA加密
     * @author leeyi <leeyisoft@qq.com>
     */
    public function encrypt($data)
    {
        // 读取public key
        $publicKey = $this->getPublicKey();
        if (empty($publicKey)) {
            return false;
        }
        if (openssl_public_encrypt($data, $encrypted, openssl_pkey_get_public($publicKey), OPENSSL_PKCS1_PADDING)) {
            $data = base64_encode($encrypted);
        } else {
            $this->error = 'Unable to encrypt data. Perhaps it is bigger than the key size?';
            return false;
        }

        return $data;
    }

    /**
     * RSA解密
     *
     * @param string $data    经过base64加密的二进制数据
     * @param int    $padding can be one of OPENSSL_PKCS1_PADDING, OPENSSL_SSLV23_PADDING, OPENSSL_PKCS1_OAEP_PADDING, OPENSSL_NO_PADDING.
     *
     * @author leeyi <leeyisoft@qq.com>
     */
    public function decrypt($data, $padding = OPENSSL_PKCS1_PADDING)
    {
        $privateKey = $this->getPrivateKey();
        if (empty($privateKey)) {
            return false;
        }
        if (openssl_private_decrypt(base64_decode($data), $decrypted, openssl_get_privatekey($privateKey), $padding)) {
            $data = $decrypted;
        } else {
            $data = false;
            $this->error = 'Unable to decrypt data.';
        }
        return $data;
    }

    /**
     * 专门针对 http://www.ohdave.com/rsa/ js 版本的 RSA 算法的解密功能
     *
     * @param Hex string $data    没有经过base64加密的十六进制字符串
     * @param int        $padding can be one of OPENSSL_PKCS1_PADDING, OPENSSL_SSLV23_PADDING, OPENSSL_PKCS1_OAEP_PADDING, OPENSSL_NO_PADDING.
     *
     * @author leeyi <leeyisoft@qq.com>
     */
    public function decryptForHex($data, $padding = OPENSSL_NO_PADDING)
    {
        $privateKey = $this->getPrivateKey();
        if (empty($privateKey)) {
            return false;
        }

        if (!openssl_private_decrypt(pack("H*", $data), $decrypted, openssl_get_privatekey($privateKey), $padding)) {
            $this->error = 'Unable to decrypt data.';
            return false;
        }
        return trim($this->utf8StrRev($decrypted));
    }

    /**
     * 设置公钥文件
     *
     * @author leeyi <leeyisoft@qq.com>
     */
    public function setPublicKeyFile($publicKeyFile)
    {
        if (empty($publicKeyFile)) {
            $this->error = '公钥不能够为空';
            return false;
        }
        if (!is_file($publicKeyFile)) {
            $publicKeyFile = dirname(__FILE__).'/mall/rsa_key/'.$publicKeyFile;
        }
        if (!is_readable($publicKeyFile)) {
            $this->error = '公钥不可读';
            return false;
        }
        $this->publicKeyFile = $publicKeyFile;
    }

    /**
     * 设置私钥文件
     *
     * @author leeyi <leeyisoft@qq.com>
     */
    public function setPrivateKeyFile($privateKeyFile)
    {
        if (empty($privateKeyFile)) {
            $this->error = '私钥不能够为空';
            return false;
        }
        if (!is_file($privateKeyFile)) {
            $privateKeyFile = dirname(__FILE__).'/mall/rsa_key/'.$privateKeyFile;
        }
        if (!is_readable($privateKeyFile)) {
            $this->error = '私钥不可读';
            return false;
        }
        $this->privateKeyFile = $privateKeyFile;
    }

    public function setPublicKey($publicKey)
    {
        $this->publicKey = $publicKey;
    }

    public function setPrivateKey($privateKey)
    {
        $this->privateKey = $privateKey;
    }

    // private

    /**
     * 翻转utf字符串
     *
     * @author leeyi <leeyisoft@qq.com>
     */
    private function utf8StrRev($str)
    {
        preg_match_all('/./us', $str, $ar);
        return join('', array_reverse($ar[0]));
    }

    /**
     * 获取公钥内容
     *
     * @author leeyi <leeyisoft@qq.com>
     */
    private function getPublicKey()
    {
        if (!empty($this->publicKey)) {
            return $this->publicKey;
        }
        if (!( is_file($this->publicKeyFile) && is_readable($this->publicKeyFile))) {
            $this->error = 'RSA签名公钥文件不存在，或者不可读。';
            return false;
        }
        //读取公钥文件
        $this->publicKey = file_get_contents($this->publicKeyFile);
        return $this->publicKey;
    }

    /**
     * 获取私钥内容
     * @author leeyi <leeyisoft@qq.com>
     */
    private function getPrivateKey()
    {
        if (!empty($this->privateKey)) {
            return $this->privateKey;
        }
        if (!(is_file($this->privateKeyFile) && is_readable($this->privateKeyFile))) {
            $this->error = 'RSA签名私钥文件不存在，或者不可读。';
            return false;
        }
        //读取私钥文件
        $this->privateKey = file_get_contents($this->privateKeyFile);
        return $this->privateKey;
    }
}

// 调用测试

// $pubfile = dirname(SITE_PATH).'/Data/key/rsa_for_app_pri_key.pem';
// $prifile = dirname(SITE_PATH).'/Data/key/rsa_for_app_pub_key.pem';

// $m = new \Org\RSAEncrypt($pubfile, $prifile);
// $x = 'dfQC0nOwhvVY9SwjIl0ZADlucFGbcY+Rc/t6/YZNaOTZHs9kvxCV1kCg6P3lOTT3+HWlJZLzcCL635eS97oq2GB4EvEwKmZiV/X9GNMvsrDKq1db5cm+mMU4VGGGQBkZWm/rln514YH1Wtk8L6xwLNZFZjxYQtRLB2/ZnTn42ZlnAqyED6KWzGaR1ho44JE/uyI78G3NRk/onCFwIuBd1rE3Re/MWYIdKBOX49WrBagDfSc2tFm32Ay6G/+0SRPoRdyQK735H2HPFFNE3EkipR2l1y0tEC8oDkC1oN6fMjUh/W4DvEPmSv3hzA2xKn25MO8wcyvqE+gitR/7tHfkvw==';
// // $a = 'hello 你好^_^';
// // $x = $m->sign($a);
// // $y = $m->verify($a, $x);
// // var_dump($x, $y);
// // $x = $m->encrypt($a);
// $y = $m->decrypt($x);
// echo $m->getError();
// var_dump($x, $y);
